package com.udf.hive.udaf;

import org.apache.hadoop.hive.ql.exec.Description;
import org.apache.hadoop.hive.ql.exec.UDFArgumentTypeException;
import org.apache.hadoop.hive.ql.metadata.HiveException;
import org.apache.hadoop.hive.ql.parse.SemanticException;
import org.apache.hadoop.hive.ql.udf.generic.AbstractGenericUDAFResolver;
import org.apache.hadoop.hive.ql.udf.generic.GenericUDAFEvaluator;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspector;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorFactory;
import org.apache.hadoop.hive.serde2.objectinspector.ObjectInspectorUtils;
import org.apache.hadoop.hive.serde2.typeinfo.TypeInfo;

import java.util.LinkedList;

/**
 * 
 * 
 * @author admin
 * @version 2017-11-20
 */
@Description(name = "udaf_collect",
value = "_FUNC_(field) - returns a collect values .",
extended = "Example:\n > select _FUNC_(field) from src;")
public final class UDAFCollect extends AbstractGenericUDAFResolver {

  public GenericUDAFEvaluator getEvaluator(TypeInfo[] parameters)
    throws SemanticException {
    if (parameters.length != 1) {
      throw new UDFArgumentTypeException(parameters.length - 1,
        "Exactly one argument is expected.");
    }

    return new GenericUDAFCollectEvaluator();
  }


  public static class GenericUDAFCollectEvaluator extends GenericUDAFEvaluator {
    ObjectInspector inputOI;
    ObjectInspector outputOI;

    @Override
    public ObjectInspector init(Mode m, ObjectInspector[] parameters)
      throws HiveException {
      assert(parameters.length == 1);
      super.init(m, parameters);
      inputOI = parameters[0];

      // This should be list of the inputOI.
      outputOI = ObjectInspectorFactory.getStandardListObjectInspector(
        ObjectInspectorUtils.getStandardObjectInspector(inputOI));
      return outputOI;
    }

    public static class UDAFCollectState implements AggregationBuffer {
      private LinkedList<Object> elements;
    }

    @Override
    public AggregationBuffer getNewAggregationBuffer()
      throws HiveException {
      UDAFCollectState myAgg = new UDAFCollectState();
      myAgg.elements = new LinkedList<Object>();
      return myAgg;
    }

    @Override
    public void reset(AggregationBuffer agg)
      throws HiveException {
      UDAFCollectState myAgg = (UDAFCollectState)agg;
      myAgg.elements.clear();
    }


    @Override
    public void iterate(AggregationBuffer agg, Object[] parameters)
      throws HiveException {
      UDAFCollectState myAgg = (UDAFCollectState)agg;
      assert(parameters.length == 1);
      if (parameters[0] != null) {
        Object pCopy = ObjectInspectorUtils.copyToStandardObject(parameters[0],
                                                                 inputOI);
        myAgg.elements.add(pCopy);
      }
    }

    @Override
    public Object terminatePartial(AggregationBuffer agg)
      throws HiveException {
      UDAFCollectState myAgg = (UDAFCollectState)agg;
      if (myAgg.elements.size() == 0) {
        return null;
      } else {
        return myAgg;
      }
    }

    @Override
    public void merge(AggregationBuffer agg, Object partial)
      throws HiveException {
      if (partial != null) {
        UDAFCollectState myAgg = (UDAFCollectState)agg;
        UDAFCollectState myPartial = (UDAFCollectState)partial;
        myAgg.elements.addAll(myPartial.elements);
      }
    }

    @Override
    public Object terminate(AggregationBuffer agg)
      throws HiveException {
      UDAFCollectState myAgg = (UDAFCollectState)agg;
      return myAgg.elements;
    }
  }
}